//
//  WKWebViewController.m
//  HTML_CSS_JS
//
//  Created by 范金龙 on 2020/4/28.
//  Copyright © 2020 FF. All rights reserved.
//

#import "WKWebViewController.h"
#import <WebKit/WebKit.h>
#import <JavaScriptCore/JavaScriptCore.h>
#import "WKDelegate.h"

@interface WKWebViewController ()
<
WKNavigationDelegate,
WKUIDelegate,
WKScriptMessageHandler
//WKDelegate
>
{
    WKUserContentController *_userContentController;
}
@property (nonatomic, strong)WKWebView *webView;
@property (strong, nonatomic)   UIProgressView              *progressView;
@end

@implementation WKWebViewController

static NSString *name = @"OCFunc";
- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"toServer"];
    [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"ScanAction"];
    [self.webView.configuration.userContentController addScriptMessageHandler:self name:name];
}
- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"toServer"];
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"ScanAction"];
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:name];
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view setBackgroundColor:[UIColor whiteColor]];
    // Do any additional setup after loading the view.

//    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
//
//    // 对js交互对象进行监听
////    addScriptMessageHandler很容易引起循环引用，导致控制器无法被释放
////    WKDelegate *wkdelegate = [[WKDelegate alloc] init];
////    wkdelegate.wkdelegate = self;// 使用delegate 破解大环引用
////    [_userContentController addScriptMessageHandler:self name:@"OCFunc"];
//    [self setWKConfig:config];
//
//    //MARK: WKWebView是获取不到JSContext的
//    self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
//    [self.view addSubview:self.webView];
//    self.webView.navigationDelegate = self;
//    self.webView.UIDelegate = self;
//
//    NSURL * baseUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
//    NSString * path = [[NSBundle mainBundle] pathForResource:@"wkDemo" ofType:@"html"];
//    NSString * html = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//    [self.webView loadHTMLString:html baseURL:baseUrl];
////    NSString *url = @"http://localhost/jinlong.com/ios.html";
////    [self.webview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]];
//    [self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
    [self initWKWebView];
    [self initProgressView];
       
   [self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)initWKWebView
{
// 使用自带的self.webview.configuration.userContentController 添加Handler，self持有self，导致的循环引用问题
//    [self.webview.configuration.userContentController addScriptMessageHandler:self name:obj];
//    重新建了一个类WKWebViewConfiguration,可以解决自循环引用问题
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    
    WKPreferences *preferences = [WKPreferences new];
    preferences.javaScriptCanOpenWindowsAutomatically = YES;
    preferences.minimumFontSize = 10.0;
    configuration.preferences = preferences;
    
    self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
    
    NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"wkDemo.html" ofType:nil];
    NSURL *fileURL = [NSURL fileURLWithPath:urlStr];
    [self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL];
    
    self.webView.UIDelegate = self;
    [self.view addSubview:self.webView];
}

- (void)setWKConfig:(WKWebViewConfiguration *)config{
    config.preferences = [[WKPreferences alloc] init];
    config.preferences.minimumFontSize = 10;
    config.preferences.javaScriptEnabled = YES;
    config.preferences.javaScriptCanOpenWindowsAutomatically = NO;
//    config.userContentController = [[WKUserContentController alloc] init];
//    config.processPool = [[WKProcessPool alloc] init];
    
    //    加载js代码
//    NSString *js = @"jsFunc(\"js\")";
//    WKUserScript *scriptObj = [[WKUserScript alloc] initWithSource:js
//                                                     injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
//                                                  forMainFrameOnly:YES];
//    _userContentController = [[WKUserContentController alloc] init];
//    [config setUserContentController:_userContentController];
//    [_userContentController addUserScript:scriptObj];
}
//MARK: WKNavigationDelegate
//该代理提供的方法，可以用来追踪加载过程（页面开始加载、加载完成、加载失败）、决定是否执行跳转
// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
    
}
// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{
    
}
// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    //向js注入关闭按钮事件监听
    NSString *script = @"document.querySelector('.close-btn').addEventListener('click',function() {window.webkit.messageHandlers.toServer.postMessage('close')},false)";
    [webView evaluateJavaScript:script completionHandler:^(id object, NSError * _Nullable error) {

        NSLog(@"evaluateJavaScript error : %@",error);
    }];
}
// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{
    
}
// MARK:-
// 页面跳转的代理方法有三种，分为（收到跳转与决定是否跳转两种）
// 接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{
    
}
// 在收到响应后，决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    decisionHandler(WKNavigationResponsePolicyAllow);
}
// 在发送请求之前，决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(nonnull WKNavigationAction *)navigationAction decisionHandler:(nonnull void (^)(WKNavigationActionPolicy))decisionHandler{
    
    decisionHandler(WKNavigationActionPolicyAllow);
}

//MARK: WKUIDelegate
// 创建一个新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{
    return self.webView;
}
//MARK:- 剩下三个代理方法全都是与界面弹出提示框相关的
//针对于web界面的三种提示框（警告框、确认框、输入框）分别对应三种代理方法。下面只举了警告框的例子。
/**
 *  web界面中有弹出警告框时调用
 *
 *  @param webView           实现该代理的webview
 *  @param message           警告框中的内容
 *  @param frame             主窗口
 *  @param completionHandler 警告框消失调用
 */
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(nonnull NSString *)message initiatedByFrame:(nonnull WKFrameInfo *)frame completionHandler:(nonnull void (^)(void))completionHandler
{
//    js 调用的alert方法，被wk拦截，在此处执行
    NSLog(@"警告框===");
    NSLog(@"alert action msg：%@",message);
    completionHandler();
}
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
    NSLog(@"确认框");
    NSLog(@"%@",message);
    completionHandler(true);
}
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
    NSLog(@"输入框");
    NSLog(@"%@",prompt);
    NSLog(@"%@",defaultText);
    completionHandler(@"输入");
}
//MARK: WKScriptMessageHandler
//这个协议中包含一个必须实现的方法，这个方法是提高App与web端交互的关键，它可以直接将接收到的JS脚本转为OC或Swift对象。（当然，在UIWebView也可以通过“曲线救国”的方式与web进行交互，著名的Cordova框架就是这种机制
// 处理来自js的方法调用
// 从web界面中接收到一个脚本时调用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    NSLog(@"message.name: %@",message.name);
    if ([message.name isEqualToString:@"ScanAction"]) {
           NSLog(@"扫一扫");
       }
    if ([message.name isEqualToString:@"OCFunc"]){//此处name为JS传出信息打包的标志<name>
        
        //用message.body获得JS传出的参数体
        //handle coding..
        /*获得传出的字符串参数，即"OCFunc-GOGOGO"*/
        NSString * dataString = message.body;
        NSLog(@"wkwebview 获取js传出的数据 message.body: %@",dataString);
        
        // 在需要调用JS的地方执行如下代码, native 回调 js 方法
        // 此处的 'postInfo()' 是前端的方法，需要前端告诉你
        // 有参数
        [self.webView evaluateJavaScript:@"postInfo('参数1,参数2')"completionHandler:nil];
        // 无参数
        [self.webView evaluateJavaScript:@"postInfo()"completionHandler:nil];
    }
}
- (void)initProgressView
{
    CGFloat kScreenWidth = [[UIScreen mainScreen] bounds].size.width;
    UIProgressView *progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 2)];
    progressView.tintColor = [UIColor redColor];
    progressView.trackTintColor = [UIColor lightGrayColor];
    [self.view addSubview:progressView];
    self.progressView = progressView;
}
#pragma mark - KVO
// 计算wkWebView进度条
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (object == self.webView && [keyPath isEqualToString:@"estimatedProgress"]) {
        CGFloat newprogress = [[change objectForKey:NSKeyValueChangeNewKey] doubleValue];
        if (newprogress == 1) {
            [self.progressView setProgress:1.0 animated:YES];
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.7 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                self.progressView.hidden = YES;
                [self.progressView setProgress:0 animated:NO];
            });
            
        }else {
            self.progressView.hidden = NO;
            [self.progressView setProgress:newprogress animated:YES];
        }
    }
}
- (void)dealloc
{
    NSLog(@"%s",__FUNCTION__);
    [self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
//    addScriptMessageHandler很容易引起循环引用，导致控制器无法被释放
    // 移除所有的 js对象
//    [self.webview.configuration.userContentController removeAllUserScripts];
    // 或
//     [self.webview.configuration.userContentController removeScriptMessageHandlerForName:@"OCFunc"];
    
}
@end
